﻿using System;
using Microsoft.SPOT;

using GT = Gadgeteer;
using GTM = Gadgeteer.Modules;
using GTI = Gadgeteer.Interfaces;
using G2Tech.CurrentCost;

namespace Gadgeteer.Modules.G2Tech
{
    /// <summary>
    /// A G2Tech.CurrentCost.Module module for Microsoft .NET Gadgeteer
    /// </summary>
    public class G2Tech_CurrentCost_Module : GTM.Module , ICurrentCostManager
    {
        #region private fields
        
        private Gadgeteer.Interfaces.Serial _serialPort = null;
        private CurrentCostManager _CurrentCostManager = null;

        #endregion

        #region constructors
        
        /// <summary></summary>
        /// <param name="socketNumber">The socket that this module is plugged in to.</param>
        public G2Tech_CurrentCost_Module(int socketNumber)
        {
            // This finds the Socket instance from the user-specified socket number.  
            // This will generate user-friendly error messages if the socket is invalid.
            // If there is more than one socket on this module, then instead of "null" for the last parameter, 
            // put text that identifies the socket to the user (e.g. "S" if there is a socket type S)
            Socket socket = Socket.GetSocket(socketNumber, true, this, "U");

            //initialize the serial port to the EnviR specification
            _serialPort = new GTI.Serial(socket, 57600, GTI.Serial.SerialParity.None, GTI.Serial.SerialStopBits.One,
                8, GTI.Serial.HardwareFlowControl.NotRequired, this);

            //initialize the Current Cost Manager 
            _CurrentCostManager = new CurrentCostManager();
            _CurrentCostManager.DataParsed += new CurrentCostManager.CurrentCostDataParsedEventHandler(_CurrentCostManager_OnDataParsed);
        } 

        #endregion

        #region public events
        
        /// <summary>
        /// On Data Parsed from Current Cost 
        /// </summary>
        public event CurrentCostManager.CurrentCostDataParsedEventHandler DataParsed;
        
        #endregion

        #region public methods
        
        /// <summary>
        /// Start receiving Data
        /// </summary>
        public void Start()
        {
            _serialPort.Open();
            _serialPort.DataReceived += new GTI.Serial.DataReceivedEventHandler(_serialPort_DataReceived);
        }

        /// <summary>
        /// Stop receiving Data
        /// </summary>
        public void Stop()
        {
            _serialPort.Close();
            _serialPort.DataReceived -= _serialPort_DataReceived;
        } 

        #endregion

        #region private stuff

        void _serialPort_DataReceived(GTI.Serial sender, System.IO.Ports.SerialData data)
        {
            var byteReceived = sender.BytesToRead;

            byte[] bytes = new byte[byteReceived];

            sender.Read(bytes, 0, byteReceived);

            _CurrentCostManager.FeedData(bytes);
        }

        void _CurrentCostManager_OnDataParsed(CurrentCostData currentCostData)
        {
            OnDataParsed(currentCostData);
        }

        /// <summary>
        /// the private data parsed delegate
        /// </summary>
        private CurrentCostManager.CurrentCostDataParsedEventHandler onDataParsed;

        protected virtual void OnDataParsed(CurrentCostData currentCostData)
        {
            if (this.onDataParsed == null)
            {
                this.onDataParsed = new CurrentCostManager.CurrentCostDataParsedEventHandler(this.OnDataParsed);
            }

            // Program.CheckAndInvoke helps event callers get onto the Dispatcher thread.  
            // If the event is null then it returns false.
            // If it is called while not on the Dispatcher thread, it returns false but also re-invokes this method on the Dispatcher.
            // If on the thread, it returns true so that the caller can execute the event.
            if (Program.CheckAndInvoke(DataParsed, this.onDataParsed, currentCostData))
            {
                this.DataParsed(currentCostData);
            }
        } 

        #endregion
    }
}
